package com.example.server.web.service.impl;

import com.example.server.web.config.SysConfig;
import com.example.server.web.domain.FactTable;
import com.example.server.web.domain.RespBean;
import com.example.server.web.domain.dto.RuleDto;
import com.example.server.web.domain.dto.RuleFullDto;
import com.example.server.web.service.FactTableService;
import com.example.server.web.service.RService;
import com.example.server.web.utils.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.Rserve.RConnection;
import org.rosuda.REngine.Rserve.RserveException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/*
 * 数据挖掘-R服务类
 *
 * @author: Zane
 * */
@Slf4j
@Service
public class RserviceImpl implements RService {


    @Autowired
    private FactTableService factTableService;

    // 根据学年生成指定的CSV文件
    @Override
    public void generateCsv(String year) {
        if("default".equals(year)){
            log.info("==================使用默认数据集，无需生成csv文件==================");
            return;
        }
        log.info("==================准备生成 {} 学年的csv文件==================",year);
        // 根据学年查询事实表的所有数据
        List<FactTable> factTableList = factTableService.queryAllDataByYear(year);
        log.info(" {} 学年，事实表数据量 {} ",year,factTableList.size());
        List<Object> data = new ArrayList<>();
        // 创建标题行
        List<String> headers = new ArrayList<>();
        headers.add("fact_id");
        headers.add("attitude_score");
        headers.add("content_score");
        headers.add("method_score");
        headers.add("result_score");
        headers.add("total_score");
        headers.add("level");
        // 添加数据行
        for(FactTable item: factTableList){
            data.add(
                    Arrays.asList(item.getFactId()
                            ,item.getAttitudeScore()
                            ,item.getContentScore()
                            ,item.getMethodScore()
                            ,item.getResultScore()
                            ,item.getTotalScore()
                            , item.getLevel())
            );
        }
        log.info(" 生成数据行 {} ",data.size());
        // 指定输出文件路径和名称
        String filePath = SysConfig.getRfile()+ "data_"+year+".csv";
        log.info(" 生成文件路径 {} ",filePath);
        ExcelUtil.exportToCSV(data, headers, filePath);
        log.info("==================生成CSV结束==================");
    }

    // 根据指定学年数据集生成规则
    @Override
    public RuleFullDto getRuleFull(String year) {

        log.info("==================准备挖掘 {} 学年的规则==================",year);
        RConnection c = null;// RConnection用于和Rserve建立连接
        //初始化返回类
        RuleFullDto ruleFullDto = new RuleFullDto();
        try {
            //获取连接
            try {
                log.info("R语言服务器地址 {} ，连接中...",SysConfig.getRurl());
                c = new RConnection(SysConfig.getRurl(),6311);
            }catch (Exception e){
                log.info("R语言服务器地址连接失败，正在连接本地R语言...",SysConfig.getRurl());
                c = new RConnection();
            }
            log.info("连接R服务器成功！");
            //加载相关类包
            c.eval("library(rpart)");
            c.eval("library(rpart.plot)");
            c.eval("library(data.table)");
            c.eval("library(jsonlite)");
            //设置种子
            c.eval("set.seed(1234)");
            //加载数据集（注意系统的文件目录 windows和linux不同）
            String dataFile = SysConfig.getRfile()+"data_"+year+".csv";
            if("default".equals(year)){
                //加载默认数据集
                dataFile = SysConfig.getRfile()+"data.csv";
            }
            c.eval("data <- fread('" + dataFile + "')");
            log.info("成功加载数据集 {} ！ ",dataFile);
            // 定义多条 R 语句
            String[] rScripts = new String[]{
                    "data <- data[,c(2:5,7)]",
                    "select <- sample(1:nrow(data),nrow(data)*0.8)",
                    "train <- data[select,]",
                    "test <- data[-select,]",

                    "CART.tree <- rpart(level ~ ., data=train)",

                    "CART.Prediction <- predict(CART.tree, newdata=test, type='class')",
                    "results <- table(Prediction=CART.Prediction, Actual=test$level)",
                    "Correct_Rate <- sum(diag(results)) / sum(results)",

                    "prediction <- predict(CART.tree, newdata = data, type = 'class')",
                    "result <- table(prediction) / length(prediction)",
                    "pa <- result['A']",
                    "pb <- result['B']",
                    "pc <- result['C']"

            };
            // 执行多条 R 语句
            for (String script : rScripts) {
                c.eval(script);
            }
            log.info("数据挖掘中...");
            //执行输出规则函数
            c.voidEval(method);
            String json_data = c.eval("result").asString();
            //解析json
            JSONObject jsonObject = new JSONObject(json_data);
            JSONArray rulesArray = jsonObject.getJSONArray("rules");
            //解析规则
            List<RuleDto> ruleList = tranJsonArraytoList(rulesArray);
            //保存模型准确率
            ruleFullDto.setAccuracy(c.eval("Correct_Rate").asDouble());
            //保存分类占比
            ruleFullDto.setPartA(c.eval("pa").asDouble());
            ruleFullDto.setPartB(c.eval("pb").asDouble());
            ruleFullDto.setPartC(c.eval("pc").asDouble());
            //筛选规则列表
            List<RuleDto> ansRuleList = getAnsRuleList(ruleList);
            //保存规则列表
            ruleFullDto.setRuleDtoList(ansRuleList);
            log.info("保存决策分类结果...");
        } catch (RserveException | REXPMismatchException e) {
            e.printStackTrace();
        } finally {
            c.close(); // 一定要这一行！！！用完一定要关！！！
        }

        log.info("==================数据挖掘-规则生成结束==================");
        return  ruleFullDto;

    }

    // 筛选10条规则作为展示
    public static List<RuleDto> getAnsRuleList(List<RuleDto> ruleList){
        //保存规则列表
        List<RuleDto> ansRuleList = new ArrayList<>();
        //保存已选择的规则
        HashSet<String> set = new HashSet<>();
        int A = 4;
        int B = 4;
        int C = 2;
        //第一次遍历优先（4条A，4条B,2条C）
        for (RuleDto item : ruleList) {
            if(A > 0 && "A".equals(item.getValue())){
                ansRuleList.add(item);
                set.add(item.getPath());
                A--;;
            }
            if(B > 0 && "B".equals(item.getValue())){
                ansRuleList.add(item);
                set.add(item.getPath());
                B--;
            }
            if(C > 0 && "C".equals(item.getValue())){
                ansRuleList.add(item);
                set.add(item.getPath());
                C--;
            }
        }
        //如果遍历所有规则后，发现某种类缺少，进行二次遍历补充其他类
        int count = ansRuleList.size();
        // 如果还没找到足够的规则，则按照顺序从其它类型中查找规则
        if (count < 10) {
            for (RuleDto item : ruleList) {
                if (count >= 10) {
                    // 已经找到10条规则，退出循环
                    break;
                }
                if (!set.contains(item.getPath())) {
                    ansRuleList.add(item);
                    set.add(item.getPath());
                    count++; // 找到一条其他类型的规则，计数器加1
                }
            }
        }
        return ansRuleList;
    };
    // 将R返回的json数据转化为List
    public static List<RuleDto> tranJsonArraytoList(JSONArray rulesArray){
        List<RuleDto> ruleList = new ArrayList<>();
        for (int i = 0; i < rulesArray.length(); i++) {
            JSONObject ruleObj = rulesArray.getJSONObject(i);
            RuleDto rule = new RuleDto();

            JSONArray valueArray = ruleObj.getJSONArray("value");
            if (valueArray.isNull(0)) {
                rule.setValue(null);
            } else {
                rule.setValue(valueArray.getString(0));
            }

            JSONArray pathArray = ruleObj.getJSONArray("path");
            List<String> pathList = new ArrayList<>();
            for (int j = 0; j < pathArray.length(); j++) {
                pathList.add(pathArray.getString(j));
            }
            String temp = pathList.toString();
            rule.setPath(adjustRule(temp));

            ruleList.add(rule);
        }
        return ruleList;
    }


    /* 调整规则：使每个判断元素取范围最小的一个 */
    public static String adjustRule(String input){
        /* 将字符串 转为 数组 方便合并*/
        // 去除首尾的方括号
        input = input.substring(1, input.length() - 1);
        // 使用逗号分割字符串
        String[] array = input.split(",");

        List<String> resultList = new ArrayList<>();
        //效果
        String lastR = null;
        //内容
        String lastC = null;
        //态度
        String lastA = null;
        //方法
        String lastM = null;

        for (String str : array) {
            if (str.contains("result")) {
                lastR = str;
            } else if (str.contains("content")) {
                lastC = str;
            } else if (str.contains("attitude")) {
                lastA = str;
            } else if (str.contains("method")) {
                lastM = str;
            }
        }
        if (lastR != null) {
            resultList.add(lastR);
        }
        if (lastC != null) {
            resultList.add(lastC);
        }
        if (lastA != null) {
            resultList.add(lastA);
        }
        if (lastM != null) {
            resultList.add(lastM);
        }

        return resultList.toString().replaceAll("^\\[\\s*|\\s*\\]$", "");

    }

    // R语言输出规则函数
    public static String method ="extract_rules_json <- function(model, compact = FALSE, classes = NULL) {\n" +
            "  if (!inherits(model, \"rpart\")) \n" +
            "    stop(\"Not a legitimate rpart tree\")\n" +
            "  \n" +
            "  rtree <- length(attr(model, \"ylevels\")) == 0\n" +
            "  target <- as.character(attr(model$terms, \"variables\")[2])\n" +
            "  frm <- model$frame\n" +
            "  names <- row.names(frm)\n" +
            "  ylevels <- attr(model, \"ylevels\")\n" +
            "  ds.size <- model$frame[1, ]$n\n" +
            "  \n" +
            "  if (rtree) \n" +
            "    ordered <- rev(sort(frm$n, index = TRUE)$ix)\n" +
            "  else ordered <- rev(sort(frm$yval2[, 5], index = TRUE)$ix)\n" +
            "  \n" +
            "  rules <- list()\n" +
            "\n" +
            "  for (i in ordered) {\n" +
            "    if (frm[i, 1] == \"<leaf>\") {\n" +
            "      if (rtree) \n" +
            "        yval <- frm[i, ]$yval\n" +
            "      else yval <- ylevels[frm[i, ]$yval]\n" +
            "      \n" +
            "      if (is.null(classes) || yval %in% classes) {\n" +
            "        cover <- frm[i, ]$n\n" +
            "        pcover <- round(100 * cover / ds.size)\n" +
            "        if (!rtree) \n" +
            "          prob <- frm[i, ]$yval2[, ifelse(yval == \"A\", 5, ifelse(yval == \"B\", 6, 7))]\n" +
            "        \n" +
            "        pth <- rpart::path.rpart(model, nodes = as.numeric(names[i]), print.it = FALSE)\n" +
            "        pth <- unlist(pth)[-1]\n" +
            "        if (!length(pth)) \n" +
            "          pth <- \"True\"\n" +
            "        \n" +
            "        rule <- list(\n" +
            "          number = names[i],\n" +
            "          target = target,\n" +
            "          value = yval,\n" +
            "          cover = pcover,\n" +
            "          path = pth\n" +
            "        )\n" +
            "        \n" +
            "        # 添加规则到列表中\n" +
            "        rules <- append(rules, list(rule))\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "  \n" +
            "  # 将规则列表转换为JSON格式并输出\n" +
            "  json_data <- toJSON(list(rules = rules))\n" +
            "  #cat(json_data)\n" +
            "  return(json_data)\n" +
            "}\n" +
            "\n" +
            "# 调用函数以输出规则的JSON格式\n" +
            "result <- extract_rules_json(CART.tree, compact = TRUE)";
}
